
	// General async handler (inspired from JSquared)
	dappr.extend({
		asyncHandle: function(options) {
			
			var timeoutLength = 12000;
			var requestObj = null;
			var requestDetails = { method: "get" };
			var timeout = null;
			var failed = false;	
			var me = this; // used in handleX methods	
			
			var failureCodes = {			
				// A general failure
				general: "xx1",
				// HTTP 401
				unauthorised: 401,
				// HTTP 404
				notFound: 404,
				// HTTP 408
				timeout: 408,
				// HTTP 500
				server: 500
			};
			
			/* allow the passing in of an object literal */
			if (typeof options == "object") {				
				requestDetails.URL = options.URL || dapprProxyURL; // by default use the dapprProxyURL address defined by the user
				requestDetails.method = options.method || requestDetails["method"];
				requestDetails.onSuccess = (typeof options.onSuccess == "function") ? options.onSuccess : null;
				requestDetails.onError = (typeof options.onError == "function") ? options.onError : null;
				requestDetails.scope = (typeof options.scope == "object") ? options.scope : null;
				requestDetails.timeoutLength = options.timeoutLength || timeoutLength;
				if ( options.headers instanceof Array ) {
					for (var i = options.headers.length-1; i>=0; i--)
						addRequestHeader( options.headers[i][0] || options.headers[i]["key"] || null, options.headers[i][1] || options.headers[i]["value"] || null );
				}
				
			}
			
			function addRequestHeader(key, value) {
				if (key != null && value != null) {
					requestDetails.requestHeaders = requestDetails.requestHeaders || {};
					requestDetails.requestHeaders[key] = value;
				}
			}
			this.addRequestHeader = addRequestHeader;
			
			
			// Send the AJAX request
			this.send = function(params, data) {
				
				var requestDetails = this.getRequestDetails();		
				
				//reset the failed notifier
				failed = false;
				//check that a URL has been specified
				if (requestDetails.URL == null || requestDetails.URL == "")
					return false;
				// create HTTP parameters with the params provided
				var requestParams = '';
				var first = true;
				
				if (params != null && !isObjEmpty(params)) {
					
					if (params != null && (typeof params) == 'string') {
						requestParams += params;
						first = false;
					}
					
					// flatten params to a string for complex params types (i.e. objects and arrays)
					if (params != null && ((typeof params) == 'object' || (typeof params) == 'array')) {
						requestParams += getComplexParams(params);
					}
					
				}
				
				function isObjEmpty(obj){
				   for(var i in obj){ return false;}
				  	return true;
				};
				
				function getComplexParams(node, parameters) {
					if(parameters==null) parameters = '?';
					for(param in node) {
						if( node[param] != null && 
							 (((typeof node[param]) == 'array' && node[param].length > 0) || 
								 ((typeof node[param]) == 'object' && !isObjEmpty(node[param]))) ) {
							parameters = getComplexParams(node[param], parameters);
						} else {
							(!first) ? parameters += '&' : first = false;
							parameters += param + '=' + node[param];
						}
					}
					return parameters;	
				};
							
				//get an XMLHttpRequest object
				requestObj = requestObj || this.getRequestObject();
				//open the request
				requestObj.open(requestDetails.method, requestDetails.URL + (requestParams!='' ? requestParams : ''), true);
				//set the correct encoding type for post requests
				if (requestDetails.method.toLowerCase() == "post")
					requestObj.setRequestHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
				//add the supplied headers to the request
				for (var key in requestDetails.requestHeaders)
					requestObj.setRequestHeader(key, requestDetails.requestHeaders[key]);
				//set the onreadystatechange handler
				requestObj.onreadystatechange = this.handleReadyStateChange;
				//initiate the timeout for handling request timeouts
				timeout = setTimeout(this.handleTimeout, requestDetails.timeoutLength);
				//send the request
				try {
					requestObj.send( data || null );
				} catch(e) {
					// trigger error immediately if send fails
					this.handleError();
				}
			}
			
			// Handles ready state changes for the AJAX request
			this.handleReadyStateChange = function() {
				//if the request has already failed, dont bother going any further
				if (failed == true) return;
				//if the response has arrived
				if (requestObj.readyState == 4) {
					//clear the timeout to avoid firing a timeout error
					me.clearTimeout();
					//if the request has returned OK
					if (requestObj.status == 200) {
						// parse data to JSON: get the request data and parse to a JSON object (always!)
						var data = requestObj.responseText;						
						if ( data != null && data != undefined && typeof JSON === "object" && JSON.parse ) {
		                    data = JSON.parse( data );
		                } else {
		                    data = (new Function("return " + data))();
		                }
						window.console.log('Data: ', data);

						// check whether we actually have an error from the PHP proxy file:
						if(data.code != 200) {
							fail(failureCodes.general);
							return;
						}

						//if a success handler has been provided, call it now and pass back in the AJAX object
						if (typeof options.onSuccess == "function")
							requestDetails.onSuccess.call(requestDetails.scope || requestDetails.onSuccess , data, requestObj.status);
					} else {
						//set the failed notifier
						failed = true;
						//if a fail handler has been provided, call it
						if (dappr.isFunction(requestDetails.onError)) {
							var status = requestObj.status;
							var failureCode = failureCodes.general;
							//calculate if a fail code matches the status of the request
							for (var failType in failureCodes) {
								if (failureCodes[failType] == status) {
									failureCode = failureCodes[failType];
									break;
								}
							}
							fail(failureCode);
						}
					}
				}
			}

			// Handle the timeout for timing out the request. Will cause the timeout failure to be fired and will close the AJAX request.
			this.handleTimeout = function() {
				me.clearTimeout();
				if (typeof options.onError == "function") {
					failed = true;
					me.close();
					fail(failureCodes.timeout);
				}
			}					

			// Handle an error in sending the request. Will cause the general failure to be fired and will close the AJAX request.
			this.handleError = function() {
				me.clearTimeout();
				if (typeof options.onError == "function") {
					failed = true;
					me.close();
					// todo: add other error codes that can be thrown
					fail(failureCodes.general);
				}
			}

			this.clearTimeout = function() {
				clearTimeout(timeout);
			}
			
			//fail the request with the given fail code or with a general error if none is supplied
			function fail(failCode) {
				failed = true;
				requestDetails.onError.call( requestDetails.scope || requestDetails.onError, failCode || failureCodes.general );
			}			

			// close the request object
			this.close = function() {
				requestObj.abort();
			}	

			// Get the request parameters
			this.getRequestDetails = function() {
				return requestDetails;
			}
			
			// Get a response header from the resulting response to the request.
			this.getResponseHeader = function(headerName) {
				return requestObj.getResponseHeader(headerName);
			}

			// Get all the resulting response headers.
			this.getAllResponseHeaders = function() {
				return requestObj.getAllResponseHeaders();
			}

			// Get the resulting response text returned from the request.
			this.getResponseText = function() {
				return requestObj.responseText;
			}

			// Get the resulting response XML if the response uses an XML mime type and is valid.
			this.getResponseXML = function() {
				return requestObj.responseXML;
			}

		}
	});

	dappr.asyncHandle.prototype.getRequestObject = function() {
		return (window.ActiveXObject ? new ActiveXObject("Microsoft.XMLHTTP") : new XMLHttpRequest());
	}

